package lecho.lib.hellocharts.renderer;

import lecho.lib.hellocharts.ChartComputator;
import lecho.lib.hellocharts.model.Line;
import lecho.lib.hellocharts.model.LineChartData;
import lecho.lib.hellocharts.model.PointValue;
import lecho.lib.hellocharts.model.ValueShape;
import lecho.lib.hellocharts.provider.LineChartDataProvider;
import lecho.lib.hellocharts.util.Utils;
import lecho.lib.hellocharts.view.Chart;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.Paint.Cap;
import android.graphics.Path;
import android.graphics.PathEffect;
import android.graphics.PorterDuff.Mode;
import android.graphics.Rect;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;

import static lecho.lib.hellocharts.renderer.ColumnChartRenderer.DEFAULT_SUBCOLUMN_SPACING_DP;

/**
 * Renderer for line chart. Can draw lines, cubic lines, filled area chart and
 * scattered chart.
 *
 * @author Leszek Wach
 */
public class LineChartRenderer extends AbstractChartRenderer {
    private static final float LINE_SMOOTHNES = 0.15f;
    private static final int DEFAULT_LINE_STROKE_WIDTH_DP = 3;
    private static final int DEFAULT_TOUCH_TOLLERANCE_MARGIN_DP = 4;

    private static final int MODE_DRAW = 0;
    private static final int MODE_HIGHLIGHT = 1;

    private LineChartDataProvider dataProvider;

    private int checkPrecission;

    private float baseValue;

    private int touchTolleranceMargin;
    private Path path = new Path();
    private Paint linePaint = new Paint();
    private Paint pointPaint = new Paint();
    private float[] valuesBuff = new float[2];

    /**
     * Not hardware accelerated bitmap used to draw Path(smooth lines and filled
     * area). Bitmap has size of contentRect so it is usually smaller than the
     * view so you should used relative coordinates to draw on it.
     */
    private Bitmap swBitmap;

    /**
     * Canvas to draw on secondBitmap.
     */
    private Canvas swCanvas = new Canvas();
    private float lX;

    private float lY;
    private int subcolumnSpacing;

    public LineChartRenderer(Context context, Chart chart,
                             LineChartDataProvider dataProvider) {
        super(context, chart);
        this.dataProvider = dataProvider;

        touchTolleranceMargin = Utils.dp2px(density,
                DEFAULT_TOUCH_TOLLERANCE_MARGIN_DP);

        linePaint.setAntiAlias(true);
        linePaint.setStyle(Paint.Style.STROKE);
        linePaint.setStrokeCap(Cap.ROUND);
        linePaint.setStrokeWidth(Utils.dp2px(density,
                DEFAULT_LINE_STROKE_WIDTH_DP));

        pointPaint.setAntiAlias(true);
        pointPaint.setStyle(Paint.Style.FILL);

        checkPrecission = Utils.dp2px(density, 2);

    }

    @Override
    public void initMaxViewport() {
        if (isViewportCalculationEnabled) {
            calculateMaxViewport();
            chart.getChartComputator().setMaxViewport(tempMaxViewport);
        }
    }

    @Override
    public void initDataMeasuremetns() {
        final ChartComputator computator = chart.getChartComputator();

        computator.setInternalMargin(calculateContentAreaMargin());
        computator.setInternalMargin(0);
        if (computator.getChartWidth() > 0 && computator.getChartHeight() > 0) {
            swBitmap = Bitmap.createBitmap(computator.getChartWidth(),
                    computator.getChartHeight(), Bitmap.Config.ARGB_8888);
            swCanvas.setBitmap(swBitmap);
        }
    }

    @Override
    public void initDataAttributes() {
        super.initDataAttributes();

        LineChartData data = dataProvider.getLineChartData();

        // Set base value for this chart - default is 0.
        baseValue = data.getBaseValue();

    }

    private Canvas mCanvas;

    private static final String TAG = "LineChartRenderer";
    @Override
    public void draw(Canvas canvas) {
        final LineChartData data = dataProvider.getLineChartData();

        final Canvas drawCanvas;
        int bgColor=data.getBgChartColor();
        // swBitmap can be null if chart is rendered in layout editor. In that
        // case use default canvas and not swCanvas.
        if (null != swBitmap) {
            drawCanvas = swCanvas;

            drawCanvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
        } else {
            drawCanvas = canvas;
        }

        if(bgColor!=0){
            drawBgChart(bgColor,calculateColumnWidth(),canvas);
        }
        drawBackground(drawCanvas,data);
        for (Line line : data.getLines()) {
            if (line.getMaxBitmap() != null && drawCanvas != null) {
               drawMaxMinBitMap(line,drawCanvas);
            }
            if(line.isOnlyDrawMaxMin()){
                drawMaxMinLaber(line,drawCanvas);
            }
            if (line.hasLines()) {
                if (line.isCubic()) {
                    drawSmoothPath(drawCanvas, line);
                    drawGoBed(drawCanvas,line);
                    drawUpBed(drawCanvas,line);
                } else {
                    drawPath(drawCanvas, line);
                }
            }
        }
        //	drawFirstCheckedPoint(canvas,2);
        if (null != swBitmap) {
            canvas.drawBitmap(swBitmap, 0, 0, null);
        }

    }

    private void drawGoBed(Canvas drawCanvas, Line line) {
        Bitmap go=line.getGoBedBitmap();
        if(line.getGoBedCount()==-1)return;
        final ChartComputator computator = chart.getChartComputator();
        PointValue linePoint = line.getValues().get(line.getGoBedCount()>=480?479:line.getGoBedCount());
        float currentPointX = computator.computeRawX(linePoint.getX());
        float currentPointY = computator.computeRawY(linePoint.getY());
        Paint paint=new Paint();
        paint.setColor(Color.parseColor("#8afffd"));

        paint.setTextSize(Utils.dp2px(DEFAULT_LABEL_MARGIN_DP,8));
        drawCanvas.drawText(getCountTime(line.getGoBedCount())
                ,currentPointX-Utils.dp2px(DEFAULT_LABEL_MARGIN_DP,10),
                currentPointY-Utils.dp2px(DEFAULT_LABEL_MARGIN_DP,15),paint);
        drawCanvas.drawBitmap(go,currentPointX-go.getWidth()/2,currentPointY-go.getHeight(),paint);
    //    drawCanvas.drawBitmap(up,x,y,linePaint);
    }
    private void drawUpBed(Canvas drawCanvas, Line line) {
        Bitmap up=line.getUpBedBitmap();
        if(line.getGoBedCount()==-1)return;

        final ChartComputator computator = chart.getChartComputator();
        PointValue linePoint = line.getValues().get(line.getUpBedCount()>=480?479:line.getUpBedCount());
        float currentPointX = computator.computeRawX(linePoint.getX());
        float currentPointY = computator.computeRawY(linePoint.getY());
        Paint paint=new Paint();
        paint.setColor(Color.parseColor("#8afffd"));
        paint.setTextSize(Utils.dp2px(DEFAULT_LABEL_MARGIN_DP,8));
        drawCanvas.drawText(getCountTime(line.getUpBedCount())
                ,currentPointX-up.getWidth()/2,currentPointY-up.getHeight(),paint);
        drawCanvas.drawBitmap(up,currentPointX-up.getWidth()/2,currentPointY-up.getHeight(),paint);
        //    drawCanvas.drawBitmap(up,x,y,linePaint);
    }
    private  String getCountTime(int count) {
        SimpleDateFormat format = new SimpleDateFormat("HH:mm");
        String base = "12:00";
        long time = 0;
        try {
            time = format.parse(base).getTime() + count *3*60*1000l;
        } catch (Exception e) { e.printStackTrace(); }
        return format.format(new Date(time));
    }
    private void drawBackground(Canvas canvas, LineChartData data) {
        Bitmap bitmap=data.getBitmap();
        if(bitmap==null)return;
        final ChartComputator computator = chart.getChartComputator();
        Rect rectSrc=new Rect(0,0,bitmap.getWidth(),bitmap.getHeight());
        Rect rectDes=new Rect();
        rectDes.left=computator.getContentRect().left;
        rectDes.right=  computator.getContentRect().right;
        rectDes.top= computator.getContentRect().top;
        rectDes.bottom= computator.getContentRect().bottom;
        linePaint.setColor(Color.WHITE);
       // linePaint.setStrokeWidth(10);
        canvas.drawBitmap(bitmap,rectSrc,rectDes,linePaint);

    }

    private  void drawMaxMinBitMap(Line line,Canvas canvas){
        final ChartComputator computator = chart.getChartComputator();
        final List<PointValue> pointValues = line.getValues();
        List<PointValue> tempValue = new ArrayList<>();
        float rangeValue = line.getRangeValue();
        for (PointValue value : pointValues) {
            if (value.getY() != rangeValue)
                tempValue.add(value);
        }
        Collections.sort(tempValue, new ComArrays());
        if (tempValue.size()!= 0) {
            PointValue pointValueMax = tempValue.get(0);
            PointValue pointValueMin = tempValue.get(tempValue.size() - 1);
            drawMinBitMap(canvas, line.getMinBitmap(), computator.computeRawX(pointValueMin.getX()), computator.computeRawY(pointValueMin.getY()));
            drawMaxBitMap(canvas, line.getMaxBitmap(), computator.computeRawX(pointValueMax.getX()), computator.computeRawY(pointValueMax.getY()));
        }
    }
    private  void drawMaxMinLaber(Line line,Canvas canvas){
        final ChartComputator computator = chart.getChartComputator();
        final List<PointValue> pointValues = line.getValues();
        List<PointValue> tempValue = new ArrayList<>();
        float rangeValue = line.getRangeValue();
        for (PointValue value : pointValues) {
            if (value.getY() != rangeValue)
                tempValue.add(value);
        }
        Collections.sort(tempValue, new ComArrays());
        if (tempValue.size()!= 0) {
            PointValue pointValueMax = tempValue.get(0);
            PointValue pointValueMin = tempValue.get(tempValue.size() - 1);
            int pointRadius=Utils.dp2px(density, line.getPointRadius());
            final int labelHeight = Math.abs(fontMetrics.ascent);
            drawLabel(canvas, line, pointValueMax, computator.computeRawX(pointValueMax.getX()), computator.computeRawY(pointValueMax.getY()),
                   pointRadius + labelOffset);
            drawLabel(canvas, line, pointValueMin,  computator.computeRawX(pointValueMin.getX()), computator.computeRawY(pointValueMin.getY())+ pointRadius + labelOffset + labelHeight + labelMargin ,
                    pointRadius + labelOffset);
          //  canvas.drawText((int)pointValueMax.getY()+"",computator.computeRawX(pointValueMax.getX())-,computator.computeRawY(pointValueMax.getY())-labelMargin*2,labelPaint);
         //   canvas.drawText((int)pointValueMin.getY()+"",computator.computeRawX(pointValueMin.getX()),computator.computeRawY(pointValueMin.getY())+labelMargin*2,labelPaint);
        }
    }
    protected float calculateColumnWidth() {
        final ChartComputator computator = chart.getChartComputator();
        // columnWidht should be at least 2 px
        float columnWidth = 0.2f * computator.getContentRect().width() / computator.getVisibleViewport().width();
        if (columnWidth < 2) {
            columnWidth = 2;
        }

        return columnWidth;
    }
    @Override
    public void drawUnclipped(Canvas canvas) {
        final LineChartData data = dataProvider.getLineChartData();
        int lineIndex = 0;

        for (Line line : data.getLines()) {

            if (line.hasPoints()) {
                drawPoints(canvas, line, lineIndex, MODE_DRAW);
            }
            ++lineIndex;
        }
        if (isTouched()) {
            // Redraw touched point to bring it to the front
            highlightPoints(canvas);
        }

    }

    @Override
    public boolean checkTouch(float touchX, float touchY) {
        selectedValue.clear();
        final LineChartData data = dataProvider.getLineChartData();
        final ChartComputator computator = chart.getChartComputator();
        int lineIndex = 0;
        for (Line line : data.getLines()) {
            int pointRadius = Utils.dp2px(density, line.getPointRadius());
            int valueIndex = 0;
            for (PointValue pointValue : line.getValues()) {
                final float rawValueX = computator.computeRawX(pointValue
                        .getX());
                final float rawValueY = computator.computeRawY(pointValue
                        .getY());
                if (isInArea(rawValueX, rawValueY, touchX, touchY, pointRadius
                        + touchTolleranceMargin)) {
                    selectedValue.set(lineIndex, valueIndex, 0);
                }
                ++valueIndex;
            }
            ++lineIndex;
        }
        return isTouched();
    }

    private void calculateMaxViewport() {
        tempMaxViewport.set(Float.MAX_VALUE, Float.MIN_VALUE, Float.MIN_VALUE,
                Float.MAX_VALUE);
        LineChartData data = dataProvider.getLineChartData();

        for (Line line : data.getLines()) {
            // Calculate max and min for viewport.
            for (PointValue pointValue : line.getValues()) {
                if (pointValue.getX() < tempMaxViewport.left) {
                    tempMaxViewport.left = pointValue.getX();
                }
                if (pointValue.getX() > tempMaxViewport.right) {
                    tempMaxViewport.right = pointValue.getX();
                }
                if (pointValue.getY() < tempMaxViewport.bottom) {
                    tempMaxViewport.bottom = pointValue.getY();
                }
                if (pointValue.getY() > tempMaxViewport.top) {
                    tempMaxViewport.top = pointValue.getY();
                }

            }
        }
    }

    private int calculateContentAreaMargin() {
        int contentAreaMargin = 0;
        final LineChartData data = dataProvider.getLineChartData();

        for (Line line : data.getLines()) {
            if (line.hasPoints()) {
                int margin = line.getPointRadius()
                        + DEFAULT_TOUCH_TOLLERANCE_MARGIN_DP;
                if (margin > contentAreaMargin) {
                    contentAreaMargin = margin;
                }
            }
        }
        return Utils.dp2px(density, contentAreaMargin);
    }

    /**
     * Draws lines, uses path for drawing filled area on secondCanvas. Line is
     * drawn with canvas.drawLines() method.
     */
    private void drawPath(Canvas canvas, final Line line) {
        final ChartComputator computator = chart.getChartComputator();

        prepareLinePaint(line);
        if (line.isDefauleLine()) {// 本来的demo
            int valueIndex = 0;
            boolean hasLine = false;
            for (PointValue pointValue : line.getValues()) {

                final float rawX = computator.computeRawX(pointValue.getX());
                final float rawY = computator.computeRawY(pointValue.getY());

                if (valueIndex == 0) {
                    path.moveTo(rawX, rawY);
                } else {
                    if (line.getShape() == ValueShape.BITMAP) {
                        if (pointValue.getY() != 0) {
                            if (hasLine) {
                                path.moveTo(rawX, rawY);
                            }
                            path.lineTo(rawX, rawY);
                            hasLine = false;
                        } else {
                            hasLine = true;
                            path.moveTo(rawX, rawY);
                        }
                    } else {
                        path.lineTo(rawX, rawY);
                    }
                }

                ++valueIndex;

            }


            if (line.isGradient()) {
                drawAreaGradient(canvas,line.getGradientColor());
            } else {
                canvas.drawPath(path, linePaint);
            }
            if (line.isFilled()) {
                drawArea(canvas, line.getAreaTransparency());
            }
            path.reset();
        } else {
            drawPathCustomer(canvas, line);
        }
    }

    private void drawPathCustomer(Canvas canvas, Line line) {
        final ChartComputator computator = chart.getChartComputator();
        final int lineSize = line.getValues().size();
        float prepreviousPointX = Float.NaN;
        float prepreviousPointY = Float.NaN;
        float previousPointX = Float.NaN;
        float previousPointY = Float.NaN;
        float currentPointX = Float.NaN;
        float currentPointY = Float.NaN;
        float nextPointX = Float.NaN;
        float nextPointY = Float.NaN;
        for (int valueIndex = 0; valueIndex < lineSize; ++valueIndex) {
            if (Float.isNaN(currentPointX)) {
                PointValue linePoint = line.getValues().get(valueIndex);
                currentPointX = computator.computeRawX(linePoint.getX());
                currentPointY = computator.computeRawY(linePoint.getY());
            }
            if (Float.isNaN(previousPointX)) {
                if (valueIndex > 0) {
                    PointValue linePoint = line.getValues().get(valueIndex - 1);
                    previousPointX = computator.computeRawX(linePoint.getX());
                    previousPointY = computator.computeRawY(linePoint.getY());
                } else {
                    previousPointX = currentPointX;
                    previousPointY = currentPointY;
                }
            }

            if (Float.isNaN(prepreviousPointX)) {
                if (valueIndex > 1) {
                    PointValue linePoint = line.getValues().get(valueIndex - 2);
                    prepreviousPointX = computator
                            .computeRawX(linePoint.getX());
                    prepreviousPointY = computator
                            .computeRawY(linePoint.getY());
                } else {
                    prepreviousPointX = previousPointX;
                    prepreviousPointY = previousPointY;
                }
            }

            // nextPoint is always new one or it is equal currentPoint.
            if (valueIndex < lineSize - 1) {
                PointValue linePoint = line.getValues().get(valueIndex + 1);
                nextPointX = computator.computeRawX(linePoint.getX());
                nextPointY = computator.computeRawY(linePoint.getY());
            } else {
                nextPointX = currentPointX;
                nextPointY = currentPointY;
            }

            // Calculate control points.
            final float firstDiffX = (currentPointX - prepreviousPointX);
            final float firstDiffY = (currentPointY - prepreviousPointY);
            final float secondDiffX = (nextPointX - previousPointX);
            final float secondDiffY = (nextPointY - previousPointY);
            final float firstControlPointX = previousPointX
                    + (LINE_SMOOTHNES * firstDiffX);
            final float firstControlPointY = previousPointY
                    + (LINE_SMOOTHNES * firstDiffY);
            final float secondControlPointX = currentPointX
                    - (LINE_SMOOTHNES * secondDiffX);
            final float secondControlPointY = currentPointY
                    - (LINE_SMOOTHNES * secondDiffY);

//			float d0 = (float) Math.sqrt(Math.pow(currentPointX - previousPointX, 2)+
//					Math.pow(currentPointY-previousPointY, 2));	// distance between p and p0
//			float x1 = Math.min(previousPointX + lX*d0, (previousPointX + currentPointX)/2); 	// min is used to avoid going too much right
//			float y1 = prepreviousPointY+ lY*d0;
//	
//			// second control point
//			
//			float d1 = (float) Math.sqrt(Math.pow(nextPointX - previousPointX, 2)+
//					Math.pow(nextPointY-prepreviousPointY, 2));	// distance between p1 and p0 (length of reference line)
//			lX = (nextPointX-previousPointX)/d1*0.3f;		// (lX,lY) is the slope of the reference line 
//			lY = (nextPointY-prepreviousPointY)/d1*0.3f;
//			float x2 = Math.max(currentPointX - lX*d0, (previousPointX + currentPointX)/2);	// max is used to avoid going too much left
//			float y2 = currentPointY - lY*d0;
            if (valueIndex == 0) {
                // Move to start point.
                path.moveTo(currentPointX, currentPointY);
            } else {
                //	path.cubicTo(x1,y1,x2, y2, currentPointX, currentPointY);
                //	path.moveTo(x, y);
                //		path.quadTo(firstControlPointX,  firstControlPointY, 	currentPointX,  currentPointY);
                path.cubicTo(firstControlPointX, firstControlPointY,
                        secondControlPointX, secondControlPointY,
                        currentPointX, currentPointY);
            }
            float currentY = line.getValues().get(valueIndex).getY();
            if (currentY > 0 && currentY < 37.4) {
                linePaint.setColor(Color.parseColor("#74e1a7"));
            } else if (currentY >= 37.4 && currentY <= 38) {
                linePaint.setColor(Color.parseColor("#f2a700"));
            } else if (currentY > 38) {
                linePaint.setColor(Color.parseColor("#f74544"));
            }
            canvas.drawPath(path, linePaint);
            path.reset();
            path.moveTo(currentPointX, currentPointY);
            // Shift values by one back to prevent recalculation of values that
            // have
            // been already calculated.
            prepreviousPointX = previousPointX;
            prepreviousPointY = previousPointY;
            previousPointX = currentPointX;
            previousPointY = currentPointY;
            currentPointX = nextPointX;
            currentPointY = nextPointY;

        }

        // canvas.drawPath(path, linePaint);
        if (line.isFilled()) {
            drawArea(canvas, line.getAreaTransparency());
        }
        path.reset();
    }

    /**
     * Draws Besier's curve. Uses path so drawing has to be done on secondCanvas
     * to avoid problem with hardware acceleration.
     */
    private void drawSmoothPath(Canvas canvas, final Line line) {
        final ChartComputator computator = chart.getChartComputator();

        prepareLinePaint(line);
        if (!line.isDefauleLine()) {
            drawPathCustomer(canvas, line);
            return;
        }
        final int lineSize = line.getValues().size();
        float prepreviousPointX = Float.NaN;
        float prepreviousPointY = Float.NaN;
        float previousPointX = Float.NaN;
        float previousPointY = Float.NaN;
        float currentPointX = Float.NaN;
        float currentPointY = Float.NaN;
        float nextPointX = Float.NaN;
        float nextPointY = Float.NaN;
        for (int valueIndex = 0; valueIndex < lineSize; ++valueIndex) {
            if (Float.isNaN(currentPointX)) {
                PointValue linePoint = line.getValues().get(valueIndex);
                currentPointX = computator.computeRawX(linePoint.getX());
                currentPointY = computator.computeRawY(linePoint.getY());
            }
            if (Float.isNaN(previousPointX)) {
                if (valueIndex > 0) {
                    PointValue linePoint = line.getValues().get(valueIndex - 1);
                    previousPointX = computator.computeRawX(linePoint.getX());
                    previousPointY = computator.computeRawY(linePoint.getY());
                } else {
                    previousPointX = currentPointX;
                    previousPointY = currentPointY;
                }
            }

            if (Float.isNaN(prepreviousPointX)) {
                if (valueIndex > 1) {
                    PointValue linePoint = line.getValues().get(valueIndex - 2);
                    prepreviousPointX = computator
                            .computeRawX(linePoint.getX());
                    prepreviousPointY = computator
                            .computeRawY(linePoint.getY());
                } else {
                    prepreviousPointX = previousPointX;
                    prepreviousPointY = previousPointY;
                }
            }

            // nextPoint is always new one or it is equal currentPoint.
            if (valueIndex < lineSize - 1) {
                PointValue linePoint = line.getValues().get(valueIndex + 1);
                nextPointX = computator.computeRawX(linePoint.getX());
                nextPointY = computator.computeRawY(linePoint.getY());
            } else {
                nextPointX = currentPointX;
                nextPointY = currentPointY;
            }

            // Calculate control points.
            final float firstDiffX = (currentPointX - prepreviousPointX);
            final float firstDiffY = (currentPointY - prepreviousPointY);
            final float secondDiffX = (nextPointX - previousPointX);
            final float secondDiffY = (nextPointY - previousPointY);
            final float firstControlPointX = previousPointX
                    + (LINE_SMOOTHNES * firstDiffX);
            final float firstControlPointY = previousPointY
                    + (LINE_SMOOTHNES * firstDiffY);
            final float secondControlPointX = currentPointX
                    - (LINE_SMOOTHNES * secondDiffX);
            final float secondControlPointY = currentPointY
                    - (LINE_SMOOTHNES * secondDiffY);

            if (valueIndex == 0) {
                // Move to start point.
                path.moveTo(currentPointX, currentPointY);
            } else {

                path.cubicTo(firstControlPointX, firstControlPointY,
                        secondControlPointX, secondControlPointY,
                        currentPointX, currentPointY);
            }

            // Shift values by one back to prevent recalculation of values that
            // have
            // been already calculated.
            prepreviousPointX = previousPointX;
            prepreviousPointY = previousPointY;
            previousPointX = currentPointX;
            previousPointY = currentPointY;
            currentPointX = nextPointX;
            currentPointY = nextPointY;
        }

        //canvas.drawPath(path, linePaint);
        if (line.isGradient()) {
            drawAreaGradient(canvas,line.getGradientColor());
        } else {
            canvas.drawPath(path, linePaint);
            if (line.isFilled()) {
                drawArea(canvas, line.getAreaTransparency());
            }
        }

        path.reset();

    }

    private void prepareLinePaint(final Line line) {
        linePaint.setStrokeWidth(Utils.dp2px(density, line.getStrokeWidth()));
        linePaint.setColor(line.getColor());
        PathEffect pathEffect = line.getPathEffect();
        if (null != pathEffect) {
            linePaint.setPathEffect(pathEffect);
        }
    }

    class ComArrays implements Comparator<PointValue> {
        public int compare(PointValue p1, PointValue p2) {
            if (p1.getY() > p2.getY()) return -1;
            else if (p1.getY() < p2.getY()) return 1;
            else return 0;
        }
    }



    private void drawPoints(Canvas canvas, Line line, int lineIndex, int mode) {
        final ChartComputator computator = chart.getChartComputator();
        pointPaint.setColor(line.getColor());
        int valueIndex = 0;

        for (PointValue pointValue : line.getValues()) {
            int pointRadius = Utils.dp2px(density, line.getPointRadius());
            final float rawX = computator.computeRawX(pointValue.getX());
            final float rawY = computator.computeRawY(pointValue.getY());
            if (computator.isWithinContentRect(rawX, rawY, checkPrecission)) {
                if (MODE_DRAW == mode) {
                    drawPoint(canvas, line, pointValue, rawX, rawY, pointRadius);
                    if (line.getHasLaberColor()) {
                        setLaberPaintColr(line.getLaberTextColor());
                    }
                    if (line.hasLabels()) {
                        drawLabel(canvas, line, pointValue, rawX, rawY,
                                pointRadius + labelOffset);
                    }

                } else if (MODE_HIGHLIGHT == mode) {
                    if (line.hasPoints())
                        highlightPoint(canvas, line, pointValue, rawX, rawY,
                                lineIndex, valueIndex);
                } else {
                    throw new IllegalStateException(
                            "Cannot process points in mode: " + mode);
                }
            }
            ++valueIndex;
        }
    }

    /**
     * 指定点显示
     *
     * @param location
     */
    public void drawFirstCheckedPoint(Canvas canvas, int location) {
        final ChartComputator computator = chart.getChartComputator();
        Line line = dataProvider.getLineChartData().getLines().get(0);
        PointValue pointValue = line.getValues().get(location);
        int pointRadius = Utils.dp2px(density, line.getPointRadius());
        final float rawX = computator.computeRawX(pointValue.getX());
        final float rawY = computator.computeRawY(pointValue.getY());
        drawLabel(canvas, line, pointValue, rawX, rawY, pointRadius
                + labelOffset);
    }

    private void drawPoint(Canvas canvas, Line line, PointValue pointValue,
                           float rawX, float rawY, float pointRadius) {
        if (ValueShape.SQUARE.equals(line.getShape())) {
            canvas.drawRect(rawX - pointRadius, rawY - pointRadius, rawX
                    + pointRadius, rawY + pointRadius, pointPaint);
        } else if (ValueShape.CIRCLE.equals(line.getShape())) {
            int pointColor=line.getPointColor();
            if(pointColor!=0)pointPaint.setColor(pointColor);
            if(pointValue.getY()!=0)
            canvas.drawCircle(rawX, rawY, pointRadius, pointPaint);

        } else if (ValueShape.BITMAP.equals(line.getShape())) {

            float x = pointValue.getY();
            if (x == 0)
                return;
            canvas.drawBitmap(line.getHeartBitmap(x), rawX - pointRadius, rawY
                    - pointRadius, pointPaint);

        } else if (ValueShape.JSTYLE.equals(line.getShape())) {
            Bitmap bitmap = line.getJstyleBitMap();
            canvas.drawBitmap(bitmap, rawX - bitmap.getWidth() / 2, rawY - bitmap.getHeight() / 2
                    , pointPaint);
        } else if (ValueShape.JSTYLE_PLUS.equals(line.getShape())) {
            float x = pointValue.getY();
            if (x == 0)
                return;
            int state = pointValue.getRangeState();
            canvas.drawBitmap(line.getHeartBitmapsTATE(state), rawX - pointRadius, rawY
                    - pointRadius, pointPaint);
        } else {
            throw new IllegalArgumentException("Invalid point shape: "
                    + line.getShape());
        }
    }

    private void drawMaxBitMap(Canvas canvas, Bitmap bitmap, float rawX, float rawY) {
        Paint paint = new Paint();
        canvas.drawBitmap(bitmap, rawX - bitmap.getWidth() / 2, rawY - bitmap.getHeight() - labelOffset
                , paint);
    }

    private void drawMinBitMap(Canvas canvas, Bitmap bitmap, float rawX, float rawY) {
        Paint paint = new Paint();
        canvas.drawBitmap(bitmap, rawX - bitmap.getWidth() / 2, rawY + bitmap.getHeight()
                , paint);
    }

    private void highlightPoints(Canvas canvas) {
        int lineIndex = selectedValue.getFirstIndex();
        Line line = dataProvider.getLineChartData().getLines().get(lineIndex);
        drawPoints(canvas, line, lineIndex, MODE_HIGHLIGHT);
    }

    private void highlightPoint(Canvas canvas, Line line,
                                PointValue pointValue, float rawX, float rawY, int lineIndex,
                                int valueIndex) {
        if (selectedValue.getFirstIndex() == lineIndex
                && selectedValue.getSecondIndex() == valueIndex) {
            int pointRadius = Utils.dp2px(density, line.getPointRadius());
            pointPaint.setColor(line.getDarkenColor());
            drawPoint(canvas, line, pointValue, rawX, rawY, pointRadius
                    + touchTolleranceMargin);
            if (line.hasLabels() || line.hasLabelsOnlyForSelected()) {
                drawLabel(canvas, line, pointValue, rawX, rawY, pointRadius
                        + labelOffset);
            }
        }
    }
    private void drawBgChart(int bgChartColor,float columnWidth,Canvas canvas) {
        subcolumnSpacing = Utils.dp2px(density, DEFAULT_SUBCOLUMN_SPACING_DP);
        final ChartComputator computator = chart.getChartComputator();
        final float rawX = computator.computeRawX(0);
        float subcolumnRawX = rawX ;
        float subcolumnWidth = (columnWidth - subcolumnSpacing )/2;
        int count= (int) ((computator.computeRawX(computator.getContentRect().right)-rawX)/subcolumnWidth);
        final float baseRawY = computator.computeRawY(baseValue);
        RectF rectF=new RectF();
        linePaint.setStrokeCap(Cap.ROUND);
        linePaint.setStrokeWidth(subcolumnWidth);
        for(int i=0;i<count;i++){
           linePaint.setColor(bgChartColor);
            rectF.left=subcolumnRawX;
            rectF.right=subcolumnRawX+subcolumnWidth;
            rectF.bottom=baseRawY-subcolumnSpacing;
            rectF.top=computator.getContentRect().top;
            subcolumnRawX += subcolumnWidth + subcolumnSpacing;
            if(canvas!=null&&computator.isWithinContentRect(rectF.left, rectF.top, checkPrecission))
                canvas.drawLine(rectF.left,baseRawY-subcolumnWidth/2-subcolumnSpacing,rectF.left,rectF.top,linePaint);
        }
    }


    private void setLaberPaintColr(int color) {
        this.labelPaint.setColor(color);
    }


    private void drawLabel(Canvas canvas, Line line, PointValue pointValue,
                           float rawX, float rawY, float offset) {
        final ChartComputator computator = chart.getChartComputator();
        final Rect contentRect = computator.getContentRect();
        valuesBuff[0] = pointValue.getX();
        valuesBuff[1] = pointValue.getY();

        final int numChars = line.getFormatter().formatValue(labelBuffer,
                valuesBuff, pointValue.getLabel());

        if (numChars == 0) {
            // No need to draw empty label
            return;
        }

        final float labelWidth = labelPaint.measureText(labelBuffer,
                labelBuffer.length - numChars, numChars);
        final int labelHeight = Math.abs(fontMetrics.ascent);
        float left = rawX - labelWidth / 2 - labelMargin;
        float right = rawX + labelWidth / 2 + labelMargin;

        float top;
        float bottom;

        if (pointValue.getY() >= baseValue) {
            top = rawY - offset - labelHeight - labelMargin * 2;
            bottom = rawY - offset;
        } else {
            top = rawY + offset;
            bottom = rawY + offset + labelHeight + labelMargin * 2;
        }

        if (top < contentRect.top) {
            top = rawY + offset;
            bottom = rawY + offset + labelHeight + labelMargin * 2;
        }
        if (bottom > contentRect.bottom) {
            top = rawY - offset - labelHeight - labelMargin * 2;
            bottom = rawY - offset;
        }
        if (left < contentRect.left) {
            left = rawX;
            right = rawX + labelWidth + labelMargin * 2;
        }
        if (right > contentRect.right) {
            left = rawX - labelWidth - labelMargin * 2;
            right = rawX;
        }

        labelBackgroundRect.set(left, top, right, bottom);
        Bitmap laberBitmap = line.getLaberBitmap();
        if (laberBitmap == null) {
            drawLabelTextAndBackground(canvas, labelBuffer, labelBuffer.length
                    - numChars, numChars, line.getDarkenColor());
        } else {
            drawLabelTextAndBackgroundBitmap(canvas, labelBuffer, labelBuffer.length - numChars, numChars, laberBitmap, rawX, rawY);
        }


    }

    private void drawArea(Canvas canvas, int transparency) {
        final ChartComputator computator = chart.getChartComputator();
        final Rect contentRect = computator.getContentRect();

        float baseRawValue = computator.computeRawY(baseValue);
        baseRawValue = Math.min(contentRect.bottom,
                Math.max(baseRawValue, contentRect.top));

        path.lineTo(contentRect.right, baseRawValue);
        path.lineTo(contentRect.left, baseRawValue);
        path.close();
        linePaint.setStyle(Paint.Style.FILL);
        linePaint.setAlpha(transparency);
        canvas.drawPath(path, linePaint);
        linePaint.setStyle(Paint.Style.STROKE);
    }

    private void drawAreaGradient(Canvas canvas,int[]gradientColor) {
        final ChartComputator computator = chart.getChartComputator();
        final Rect contentRect = computator.getContentRect();
        float baseRawValue = computator.computeRawY(baseValue);
        baseRawValue = Math.min(contentRect.bottom,
                Math.max(baseRawValue, contentRect.top));

        path.lineTo(contentRect.right, baseRawValue);
        path.lineTo(contentRect.left, baseRawValue);
        path.close();
        //linePaint.set

        LinearGradient linearGradient = new LinearGradient(contentRect.left, 0, contentRect.left, baseRawValue,
                gradientColor, null, Shader.TileMode.MIRROR);
        linePaint.setShader(linearGradient);
        linePaint.setStrokeWidth(0);
        linePaint.setStyle(Paint.Style.FILL);
        canvas.drawPath(path, linePaint);
        linePaint.setStyle(Paint.Style.STROKE);
    }

    private boolean isInArea(float x, float y, float touchX, float touchY,
                             float radius) {
        float diffX = touchX - x;
        float diffY = touchY - y;
        return Math.pow(diffX, 2) + Math.pow(diffY, 2) <= 2 * Math.pow(radius,
                2);
    }

}
